import type {
  IconConfig,
} from '../../../../../../lib/icons/types/interfaces'
import type { PageType } from '../../../../../../lib/page_handler/type'
import type { PlatformTypeValue } from '../../../../../../types/interfaces'
import type {
  SearchAdBadgeOptions,
  SearchPageTitleCleanupConfig,
  SearchSelectorConfig,
  TitleCleanupConfig,
} from '../index'
import { PageManager } from '../../../../../../lib/abstract/page_manager'
import { IconService } from '../../../../../../lib/icons'
import { PageHandler } from '../../../../../../lib/page_handler'
import { BingPageType } from '../../../../../../lib/page_handler/platform/bing/types'
import { SoPageType } from '../../../../../../lib/page_handler/platform/so/types'
import {
  UNDEFINED_PAGE_TYPE,
  UNDEFINED_PAGE_TYPE_TIPS,
} from '../../../../../../lib/page_handler/type'
import { PollingHandler } from '../../../../../../lib/polling_handler'
import { ToolKit } from '../../../../../../lib/utils/toolkit'
import {
  AppStyleCollections,
  HOSTNAME,
  logger,
  loggerDeduplicator,
  SUPPORTED_PLATFORMS,
} from '../../../../../../types/constants'
import {
  BADGE_ELEMENT_STYLES,
  BADGE_STYLES,
} from '../../../../../../types/constants/styles/badge'
import { ConfigManager } from '../../../../../config_manager'
import { AppModule } from '../../../../../config_manager/types'

export interface SearchSourceOptions {
  type?: string
  content: string
  iconContent: string
  originalSourceElement?: JQuery<HTMLElement>
  onlyHide?: boolean
  enableIcon?: boolean
  iconConfig?: IconConfig[]
}

export enum SearchMetaLeftType {
  Normal = 'normal',
  Source = 'source',
  Score = 'score',
}

export enum SearchMetaRightType {
  Normal = 'normal',
  Time = 'time',
}

/**
 *  搜索元数据类型配置
 */
export interface SearchMetaTypeConfig {
  [key: string]: {
    leftType: SearchMetaLeftType
    rightType: SearchMetaRightType
  }
}
export type PageSearchMetaTypeConfig = Partial<Record<string, SearchMetaTypeConfig>>

/**
 *  搜索来源元数据配置
 */
export interface SearchMetaSourceConfig {
  [key: string]: {
    selector: string
    container: string
    cleanFunc?: (text: string) => string
    onlyHide?: boolean
    excludeTags?: string[]
    iconSelector?: string
    enableIcon?: boolean
    iconConfig?: IconConfig[]
  }
}
export type PageSearchMetaSourceConfig = Partial<Record<string, SearchMetaSourceConfig>>

/**
 *  搜索时间元数据配置
 */
export interface SearchMetaTimeConfig {
  selector: string
  cleanFunc?: (text: string) => string
  deleteFunc?: (text: string) => string
  excludeText?: string[]
  excludeTags?: string[]
  hideSource?: boolean
}
export type PageSearchMetaTimeConfig = Partial<Record<string, SearchMetaTimeConfig>>

/**
 *  搜索一般元数据配置
 */
export interface SearchMetaNormalConfig {
  selector: string
}
export type PageSearchMetaNormalConfig = Partial<Record<string, SearchMetaNormalConfig>>

/**
 *  搜索关注元数据配置
 */
export interface SearchMetaFollowConfig {
  selector: string
}
export type PageSearchMetaFollowConfig = Partial<Record<string, SearchMetaFollowConfig>>

/**
 *  搜索配置
 */
export interface PageSearchMetaOptions {
  leftType?: SearchMetaLeftType
  rightType?: SearchMetaRightType
  leftConfig: PageSearchMetaSourceConfig | PageSearchMetaNormalConfig
  rightConfig: PageSearchMetaTimeConfig | PageSearchMetaNormalConfig
  followConfig?: PageSearchMetaFollowConfig
  // 是否打印未支持的视频来源图标警告
  printIconUnsupportedWarning?: boolean
}

export class SearchStyle {
  private readonly name: string = 'SearchStyle'
  private static instance: SearchStyle
  private pageHandler: PageHandler = PageHandler.getInstance()
  private pageManager: PageManager = PageManager.getInstance()
  private iconService: IconService = IconService.getInstance()
  private pollingHandler: PollingHandler = PollingHandler.getInstance()
  private readonly pageType: PageType = PageHandler.getCurrentPageType()
  private unsupportedVideoSourceIcon = new Set<string>()
  // 最小值、理想值、最大值
  private readonly resultDynamicWidth: string = AppStyleCollections.Search.value.ITEM_DYNAMIC_WIDTH
  private readonly VIDEO_SOURCE_ICON_CONFIG: IconConfig[] = this.iconService.api.icons.videoSource
  get resultWidth(): string {
    const config: Partial<Record<PlatformTypeValue, Record<string, string>>> = {
      [SUPPORTED_PLATFORMS.BAI_DU]: {
        [BingPageType.Video]: AppStyleCollections.Search.value.ITEM_WIDTH_FOR_BAI_DU,
      },
      [SUPPORTED_PLATFORMS.BING]: {
        [BingPageType.Video]: AppStyleCollections.Search.value.ITEM_WIDTH_FOR_BING,
      },
      [SUPPORTED_PLATFORMS.SO]: {
        [SoPageType.Video]: AppStyleCollections.Search.value.ITEM_WIDTH_FOR_SO,
      },
      [SUPPORTED_PLATFORMS.SO_GOU]: {
        [SoPageType.Video]: AppStyleCollections.Search.value.ITEM_WIDTH_FOR_SO_GOU,
      },
    }

    return config[HOSTNAME]?.[this.pageType] || AppStyleCollections.Search.value.ITEM_WIDTH
  }

  // 搜索元数据配置
  private readonly followConfig = {
    selector: `.${AppStyleCollections.Search.class.BUTTON_FOLLOW}`,
    html: (content?: string) => `
      <button class="${AppStyleCollections.Search.class.BUTTON_FOLLOW}" style="all: initial !important;">
        ${content || ''}
      </button>
    `,
  }

  private readonly sourceConfig = {
    selector: `.${AppStyleCollections.Search.class.META_SOURCE}`,
    html: (type: string, content: string, iconClass: string) => `
      <div class="${AppStyleCollections.Search.class.META_SOURCE} ${iconClass || ''}" data-type="${type}">
        ${content}
      </div>
    `,
  }

  private readonly timeConfig = {
    selector: `.${AppStyleCollections.Search.class.META_TIME}`,
    html: (title: string, content: string) => `
      <div class="${AppStyleCollections.Search.class.META_TIME}" ${title}>
        ${content}
      </div>
    `,
  }

  /**
   * 获取单例实例
   */
  public static getInstance(): SearchStyle {
    if (!SearchStyle.instance) {
      SearchStyle.instance = new SearchStyle()
    }
    return SearchStyle.instance
  }

  /**
   * 执行初始化，包含：动态搜索结果页面
   */
  executeInit(selector: string, dynamicPages: PageType[], callback: () => void): void {
    dynamicPages.includes(this.pageType) ? this.pollingHandler.api.awaitElement(selector, callback) : callback()
  }

  /**
   * 设置基础样式
   */
  setBaseStyle(selectors: SearchSelectorConfig, options: {
    supportPages?: PageType[]
    forceUpdate?: boolean
  }): void {
    const { supportPages = [], forceUpdate = false } = options
    const columns = this.pageManager.getSearchResultColumns(true)
    const { container, selector } = selectors

    if (!ConfigManager.getInstance().getFeatureEnabled(AppModule.ThemeStyle.id)) {
      logger.warn(`[${this.name}] 主题样式功能未开启，无法应用搜索样式`)
      return
    }

    if (!container || !selector) {
      if (this.pageType === UNDEFINED_PAGE_TYPE) {
        loggerDeduplicator.warn(UNDEFINED_PAGE_TYPE_TIPS)
      }
      else {
        logger.warn(`[${this.name}] 未配置搜索，跳过设置搜索样式`)
      }
      return
    }

    if (!supportPages.includes(this.pageType))
      return

    // 强制更新时重置处理标识
    if (forceUpdate) {
      $(container).attr(AppStyleCollections.Search.attr.PROCESSED_COLUMNS, 'false')
      $(selector).attr(AppStyleCollections.Search.attr.PROCESSED_WIDTH, 'false')
    }

    this.setResultColumns(container, columns)
    this.setResultWidth(selector, columns)
    logger.important(`[${this.name}] 更新搜索样式：`, selectors)
  }

  /**
   * 设置基础样式
   */
  setResultColumns(selector: string, columns: number): void {
    if (!selector)
      return

    // 设置标识
    $(document.body).attr(AppStyleCollections.Search.attr.IS_MULTI_COLUMN, columns === 1 ? 'false' : 'true')

    $(selector)
      .not(`[${AppStyleCollections.Search.attr.PROCESSED_COLUMNS}="true"]`)
      .css({ 'grid-template-columns': `repeat(${columns}, auto)` })
      .attr(AppStyleCollections.Search.attr.PROCESSED_COLUMNS, 'true')
  }

  /**
   * 设置搜索结果宽度
   */
  setResultWidth(selector: string, columns: number): void {
    $(selector).not(`[${AppStyleCollections.Search.attr.PROCESSED_WIDTH}="true"]`).each((_index, element) => {
      element.style.setProperty('width', columns === 1 ? this.resultDynamicWidth : this.resultWidth, 'important')
      element.style.setProperty('min-width', columns === 1 ? this.resultWidth : '', 'important')
      element.style.setProperty('max-width', columns === 1 ? '' : this.resultWidth, 'important')

      // 标记为已处理
      $(element).attr(AppStyleCollections.Search.attr.PROCESSED_WIDTH, 'true')
    })
  }

  /**
   * 添加广告徽章
   */
  addAdBadge(options: SearchAdBadgeOptions): void {
    const { onPrepare, config } = options
    const { container, itemTitle = '' } = config || {}

    onPrepare?.(config)

    if (!container) {
      logger.warn(`[${this.name}] 未配置广告结果，无法添加广告标识`)
      return
    }

    if (!itemTitle) {
      logger.warn(`[${this.name}] 未配置广告结果-标题，无法添加广告标识`)
      return
    }

    if (!$(container).length) {
      logger.warn(`[${this.name}] 未发现广告结果，跳过添加广告标识：${container}`)
      return
    }

    const $adTitles = $(itemTitle)

    if (!$adTitles || $adTitles.length === 0) {
      logger.warn(`[${this.name}] 未找到广告结果标题，跳过添加广告标识：${itemTitle}`)
      return
    }

    $adTitles.each((_, el) => {
      const $adTitle = $(el)

      this.pageHandler.features.badge.addBadge(
        $adTitle,
        'ad',
        '广告',
        {
          backgroundColor: BADGE_STYLES.warning,
          elementStyle: BADGE_ELEMENT_STYLES.default,
        },
      )
    })

    logger.attention(`[${this.name}] 广告标识已添加`, itemTitle)
  }

  /**
   * 创建结果元数据
   */
  createResultMeta(selector: string, options: PageSearchMetaOptions): void {
    const { leftType = SearchMetaLeftType.Source, rightType = SearchMetaRightType.Time, leftConfig, rightConfig, followConfig, printIconUnsupportedWarning = false } = options
    const $resultItems = $(selector)

    if (!$resultItems || $resultItems.length === 0) {
      if (this.pageType === UNDEFINED_PAGE_TYPE) {
        loggerDeduplicator.warn(UNDEFINED_PAGE_TYPE_TIPS)
      }
      else {
        logger.warn(`[${this.name}] 未找到搜索结果项：${selector ? `${selector}` : '未配置选择器'}`)
      }
      return
    }

    $resultItems.not(`[${AppStyleCollections.Search.attr.PROCESSED_META}="true"]`).each((_index, element) => {
      const $element = $(element)

      // 标记为已处理
      $element.attr(AppStyleCollections.Search.attr.PROCESSED_META, 'true')

      // 创建元数据容器
      const $metaContainer = this.createMetaContainer($element)

      if (leftType === SearchMetaLeftType.Normal) {
        this.addContent($element, $metaContainer, leftConfig as PageSearchMetaNormalConfig)
      }

      if (rightType === SearchMetaRightType.Normal) {
        this.addContent($element, $metaContainer, rightConfig as PageSearchMetaNormalConfig)
      }

      if (leftType === SearchMetaLeftType.Source) {
        // 添加来源
        this.addSource($element, $metaContainer, leftConfig as PageSearchMetaSourceConfig)
      }

      if (rightType === SearchMetaRightType.Time) {
        // 添加时间
        this.addTime($element, $metaContainer, rightConfig as PageSearchMetaTimeConfig)
      }

      // 添加关注按钮
      if (followConfig)
        this.addFollowButton($element, $metaContainer, followConfig)

      // 移除空容器
      this.removeEmptyMetaContainer($metaContainer)
    })

    logger.important(`[${this.name}] 结果元数据已创建`, selector)

    if (printIconUnsupportedWarning && this.unsupportedVideoSourceIcon.size > 0)
      loggerDeduplicator.warn(`[${this.name}] 未支持的视频来源图标：${Array.from(this.unsupportedVideoSourceIcon).filter(Boolean).join('、')}`)
  }

  /**
   * 添加普通内容
   */
  addContent($element: JQuery<HTMLElement>, $container: JQuery<HTMLElement>, config: PageSearchMetaNormalConfig): void {
    const pageConfig = config[this.pageType]

    if (!pageConfig)
      return

    const { selector } = pageConfig
    const $target = $element.find(selector)

    if (!$target || $target.length === 0)
      return

    const text = $target.text().trim() || ''
    $container.append($(`<div>${text}</div>`))

    // 隐藏原元素
    ToolKit.dom.showControl($target, { show: false })
  }

  /**
   * 添加来源
   */
  addSource(
    $element: JQuery<HTMLElement>,
    $container: JQuery<HTMLElement>,
    config: PageSearchMetaSourceConfig,
  ): void {
    const pageConfig = config[this.pageType]

    if (!pageConfig)
      return

    Object.entries(pageConfig).forEach(([key, item]) => {
      const { selector, container, cleanFunc, onlyHide, excludeTags, iconSelector, enableIcon, iconConfig } = item
      const $source = $element.find(selector)
      let cleanedText = ''

      // 来源元素必须唯一，避免会有多个来源导致文本重复
      if ($source && $source.length === 1) {
        cleanedText = ToolKit.ui.element.getElementText($source, { excludeTags })
          .replace(/\d{4}[年-]\d{1,2}[月-]\d{1,2}日?/g, '')
          .trim()
          .replace(/^-|-$/g, '')
          .replace(/&nbsp;/g, '')
          .trim()
      }

      if (cleanFunc)
        cleanedText = cleanFunc(cleanedText)

      let iconContent = cleanedText

      if (iconSelector) {
        const $icon = $element.find(iconSelector)

        if ($icon && $icon.length > 0) {
          iconContent = ToolKit.ui.element.getElementText($icon, { excludeTags })
          ToolKit.dom.showControl($icon, { show: false })
        }
      }

      this.addSearchResultSource($container, {
        type: key,
        content: cleanedText,
        iconContent,
        originalSourceElement: $source.closest(container),
        onlyHide,
        enableIcon,
        iconConfig,
      })
    })
  }

  /**
   * 添加时间
   */
  addTime($element: JQuery<HTMLElement>, $container: JQuery<HTMLElement>, config: PageSearchMetaTimeConfig): void {
    const pageConfig = config[this.pageType]

    if (!pageConfig)
      return

    const { selector, cleanFunc, deleteFunc, excludeText, excludeTags, hideSource } = pageConfig
    const $timeElement = $element.find(selector)

    if (!$timeElement || $timeElement.length === 0)
      return

    let timeContent = ToolKit.ui.element.getElementText($timeElement, { excludeTags })

    // 使用自定义清理函数
    if (cleanFunc) {
      timeContent = cleanFunc(timeContent)
    }

    if (excludeText?.some((text: string) => timeContent.includes(text))) {
      return // 如果包含排除文本，直接返回不处理
    }

    const originalText = timeContent.replace(/^-|-$/g, '').trim()
    const timeTitle = ToolKit.time.isValidDateText(originalText)
      ? `title="${originalText}"`
      : ''

    timeContent = ToolKit.time.isValidTimeText(timeContent) ? ToolKit.time.convertToRelativeTime(timeContent) : timeContent

    if (!timeContent)
      return

    // 删除信息
    if (deleteFunc) {
      const originalHtml = $timeElement.html()
      const cleanedHtml = deleteFunc(originalHtml)

      if (cleanedHtml !== originalHtml) {
        $timeElement.html(cleanedHtml)
      }
    }

    // 添加时间
    this.addSearchResultTime($container, { title: timeTitle, content: timeContent, originalTimeElement: $timeElement, hideSource })
  }

  /**
   * 添加关注按钮
   */
  addFollowButton($element: JQuery<HTMLElement>, $container: JQuery<HTMLElement>, config: PageSearchMetaFollowConfig): void {
    const pageConfig = config[this.pageType]

    if (!pageConfig)
      return

    if ($container.find(this.followConfig.selector).length)
      return

    const $button = $element.find(pageConfig.selector)
    const $buttonContainer = $(this.followConfig.html())
    $buttonContainer.append($button.clone(true))

    $container.prepend($buttonContainer)

    // 隐藏原有的关注按钮
    ToolKit.dom.showControl($button, { show: false })
  }

  /**
   * 创建元数据容器
   */
  createMetaContainer($element: JQuery<HTMLElement>): JQuery<HTMLElement> {
    let $container = $element.find(`.${AppStyleCollections.Search.class.META_CONTAINER}`)

    // 创建容器
    if (!$container || $container.length === 0) {
      $container = $(`
        <div class="${AppStyleCollections.Search.class.META_CONTAINER}"></div>
      `)
      $element.append($container)
    }

    // 确保元素容器位置为相对定位
    $element[0].style.setProperty('position', 'relative', 'important')

    return $container
  }

  /**
   * 添加搜索结果来源
   */
  addSearchResultSource(
    $container: JQuery<HTMLElement>,
    options: SearchSourceOptions,
  ): void {
    if ($container.find(this.sourceConfig.selector).length)
      return

    const {
      content = '',
      iconContent = '',
      type = 'default',
      originalSourceElement,
      enableIcon = false,
      iconConfig = this.VIDEO_SOURCE_ICON_CONFIG,
      onlyHide,
    } = options || {}

    // 隐藏原有的来源
    if (originalSourceElement) {
      ToolKit.dom.showControl(originalSourceElement, { show: false })
    }

    if (onlyHide)
      return

    let iconClass = ''

    if (enableIcon) {
      const matchedIcon = iconConfig.find(item => ToolKit.string.isMatch(iconContent, item.match?.text || ''))
      const supportIcon = this.VIDEO_SOURCE_ICON_CONFIG.find(item => item.id === matchedIcon?.id)

      if (iconConfig && !supportIcon) {
        this.unsupportedVideoSourceIcon.add(iconContent)
      }

      iconClass = supportIcon && matchedIcon ? IconService.getIconClass(matchedIcon.id) : ''
    }

    if (!content)
      return

    $container.append($(this.sourceConfig.html(type, content, iconClass)))
  }

  /**
   * 添加搜索结果时间
   */
  addSearchResultTime(
    $container: JQuery<HTMLElement>,
    options: {
      title?: string
      content: string
      originalTimeElement?: JQuery<HTMLElement>
      hideSource?: boolean
    },
  ): void {
    if ($container.find(this.timeConfig.selector).length)
      return

    const { title = '', content = '', originalTimeElement, hideSource = true } = options || {}
    $container.append($(this.timeConfig.html(title, content)))

    // 隐藏原始时间元素
    if (hideSource)
      originalTimeElement?.hide()
  }

  /**
   * 移除空的元数据容器
   */
  removeEmptyMetaContainer($container: JQuery<HTMLElement>): void {
    // 移除空容器
    if (ToolKit.ui.element.hasNoChildren($container)) {
      $container.remove()
    }
  }

  /**
   * 更新标题
   */
  updateTitle(options: {
    config: SearchPageTitleCleanupConfig
    container: string
    titleSelector: string
    syncHighlight?: {
      enabled: boolean
      contentSelector: string
    }
    highlightKeywords?: string[] | string
  }): void {
    const { config, container, titleSelector, syncHighlight = { enabled: false, contentSelector: '' }, highlightKeywords = [] } = options
    const currentConfig = config[this.pageType as PageType]

    $(container)
      .not(`[${AppStyleCollections.Search.attr.PROCESSED_TITLE}="true"]`)
      .each((_, el) => {
        const $title = $(el).find(titleSelector)
        const originalTitle = $title.text().trim()
        const finalHighlightKeywords = this.initHighlightKeywords(highlightKeywords)

        // 标记为已处理
        $(el).attr(AppStyleCollections.Search.attr.PROCESSED_TITLE, 'true')

        if (!originalTitle || originalTitle.length === 0)
          return

        if (currentConfig)
          $title.text(this.cleanTitle(originalTitle, currentConfig))

        if (syncHighlight.enabled && syncHighlight.contentSelector) {
          $(el).find(syncHighlight.contentSelector).each((_, el) => {
            const $content = $(el)
            const content = $content.text().trim()

            if (!content || content.length === 0)
              return

            finalHighlightKeywords.add(content)
          })
        }

        this.highlightElement($title, [...finalHighlightKeywords])
      })

    logger.important(`[${this.name}] 标题已更新：`, titleSelector)
  }

  initHighlightKeywords(keywords: string[] | string): Set<string> {
    const finalKeywords = new Set<string>()

    if (Array.isArray(keywords)) {
      keywords.forEach(keyword => finalKeywords.add(keyword.trim()))
    }
    else if (typeof keywords === 'string' && keywords.trim()) {
      finalKeywords.add(keywords.trim())
    }

    return finalKeywords
  }

  /**
   * 标题清理
   */
  private cleanTitle(title: string, config: TitleCleanupConfig): string {
    const { platformName = '', trailingChars = '_-' } = config

    // 1. 移除平台名称
    let result = title.replace(
      new RegExp(`${ToolKit.string.escapeRegExp(platformName)}\\s*$`, 'i'), // 不区分大小写
      '',
    ).trim()

    // 2. 移除尾部特殊字符
    if (trailingChars) {
      result = result.replace(
        new RegExp(`[${ToolKit.string.escapeRegExp(trailingChars)}]+$`),
        '',
      )
    }

    return result.trim()
  }

  highlightElement($element: JQuery<HTMLElement>, highlightKeywords: string[]): void {
    if (!highlightKeywords.length || !$element.length)
      return

    // 1. 去重并过滤空关键词
    const uniqueKeywords = [...new Set(highlightKeywords.filter(k => k.trim()))]

    if (!uniqueKeywords.length)
      return

    // 2. 获取原始HTML并解码实体（防止双重编码）
    const originalHtml = $element.html() || ''
    const decodedHtml = $('<div/>').html(originalHtml).text()

    // 3. 构造正则表达式（一次性匹配所有关键词）
    const regex = new RegExp(
      `(${uniqueKeywords.map(ToolKit.string.escapeRegExp).join('|')})`,
      'gi',
    )

    // 4. 执行替换（避免重复包裹已高亮内容）
    let highlightedHtml = decodedHtml
    if (!highlightedHtml.includes(`<${AppStyleCollections.Search.value.HIGHLIGHT_TAG}>`)) {
      highlightedHtml = highlightedHtml.replace(
        regex,
        `<${AppStyleCollections.Search.value.HIGHLIGHT_TAG} class="${AppStyleCollections.Search.class.HIGHLIGHT_CLASS}">$1</${AppStyleCollections.Search.value.HIGHLIGHT_TAG}>`,
      )
    }

    // 5. 安全更新 DOM（仅当内容变化时）
    if (highlightedHtml !== originalHtml) {
      $element.html(highlightedHtml)
    }
  }
}
